package cn.lsoft.undoner.dao.redis.impl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Repository;

import cn.lsoft.undoner.dao.redis.RedisDao;

import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.util.*;


/** 
* @ClassName: RedisDaoImpl 
* @Description: TODO(这里用一句话描述这个类的作用) 
* @author undoner@gmail.com
* @date 2017年1月21日 下午2:08:51 
*/
@Repository
public class RedisDaoImpl implements RedisDao {

    @Autowired
    private RedisTemplate<Serializable, Serializable> redisTemplate;




    private int dbIdx=0;
    @Override
    public void hmset(final String key,final Map<String, String> map) {
        redisTemplate.execute(new RedisCallback<Object>() {
            @Override
            public Object doInRedis(RedisConnection connection)
                    throws DataAccessException {
                connection.select(dbIdx);
                try {
                    connection.hMSet(key.getBytes(), fromStringToByte(map));
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                }
                return null;
            }
        });
    }

    @Override
    public long hLen(final String key) {
        return (long)redisTemplate.execute(new RedisCallback<Object>() {
            @Override
            public Long doInRedis(RedisConnection connection)
                    throws DataAccessException {
                connection.select(dbIdx);
                long rel = connection.hLen(key.getBytes());
                return rel;
            }
        });
    }

    @Override
    public String get(final String key) {
        return redisTemplate.execute(new RedisCallback<String>() {
            @Override
            public String doInRedis(RedisConnection connection) throws DataAccessException {
                connection.select(dbIdx);
                byte[] val = connection.get(key.getBytes());
                try {
                    return new String(val,"UTF-8");
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                }
                return null;
            }
        });
    }

    @Override
    public void set(final String key,final String val) {

        redisTemplate.execute(new RedisCallback<Object>() {
            @Override
            public Object doInRedis(RedisConnection connection) throws DataAccessException {
                connection.select(dbIdx);
                try {
                    connection.set(key.getBytes(),val.getBytes("UTF-8"));
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                }
                return null;
            }
        });
    }

    @Override
    public Map<String, Object> hgetAllObj(final String key) {
        return (Map<String,Object>)redisTemplate.execute(new RedisCallback<Object>() {
            @Override
            public Map<String,Object> doInRedis(RedisConnection connection)
                    throws DataAccessException {
                connection.select(dbIdx);

                Map<String,Object> map = fromByteToStringAndObject(connection.hGetAll(key.getBytes()));
                return map;
            }
        });
    }

    @Override
    @SuppressWarnings("unchecked")
    public Map<String,String> hgetAll(final String key) {
        return (Map<String,String>)redisTemplate.execute(new RedisCallback<Object>() {
            @Override
            public Map<String,String> doInRedis(RedisConnection connection)
                    throws DataAccessException {
                connection.select(dbIdx);
                Map<String,String> map = null;
                try {
                    map = fromByteToString(connection.hGetAll(key.getBytes()));
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                }
                return map;
            }
        });
    }

    @Override
    public void sadd(final String key,final String value) {
        redisTemplate.execute(new RedisCallback<Object>() {
            @Override
            public Object doInRedis(RedisConnection connection)
                    throws DataAccessException {
                connection.select(dbIdx);
                try {
                    connection.sAdd(key.getBytes(), value.getBytes("UTF-8"));
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                }
                return null;
            }
        });
    }

    @Override
    @SuppressWarnings("unchecked")
    public Set<String> smembers(final String key) {
        return (Set<String>)redisTemplate.execute(new RedisCallback<Object>() {
            @Override
            public Set<String> doInRedis(RedisConnection connection)
                    throws DataAccessException {
                connection.select(dbIdx);
                Set<byte[]> set = connection.sMembers(key.getBytes());
                try {
                    return fromByteToString(set);
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                }
                return null;
            }
        });
    }

    @Override
    public boolean exists(final String key) {
        return (boolean)redisTemplate.execute(new RedisCallback<Object>() {
            @Override
            public Boolean doInRedis(RedisConnection connection)
                    throws DataAccessException {
                connection.select(dbIdx);
                boolean exists = connection.exists(key.getBytes());
                return exists;
            }
        });
    }
    @Override
    public void del(final String key) {
        redisTemplate.execute(new RedisCallback<Object>() {
            @Override
            public Object doInRedis(RedisConnection connection)
                    throws DataAccessException {
                connection.select(dbIdx);
                connection.del(key.getBytes());
                return null;
            }
        });
    }

    @Override
    public void hdel(final String key,final String hkey) {
        redisTemplate.execute(new RedisCallback<Object>() {
            @Override
            public Object doInRedis(RedisConnection connection) throws DataAccessException {
                connection.select(dbIdx);
                connection.hDel(key.getBytes(), hkey.getBytes());
                return null;
            }
        });
    }

    @Override
    public void select(final int dbId) {
        dbIdx = dbId;
//        redisTemplate.execute(new RedisCallback<Object>() {
//            @Override
//            public Object doInRedis(RedisConnection connection)
//                    throws DataAccessException {
//                connection.select(dbId);
//                return null;
//            }
//        });
    }

    private Set<String> fromByteToString(Set<byte[]> set) throws UnsupportedEncodingException {
        Set<String> resultSet = new HashSet<String>();
        for(byte[] value : set) {
            resultSet.add(new String(value,"UTF-8"));
        }
        return resultSet;
    }

    private Map<byte[],byte[]> fromStringToByte(Map<String,String> map) throws UnsupportedEncodingException {
        Map<byte[],byte[]> resultMap = new HashMap<byte[], byte[]>();
        Set<String> keySet = map.keySet();
        for(String key : keySet) {
            String value = map.get(key);
            resultMap.put(key.getBytes(), value.getBytes("UTF-8"));
        }
        return resultMap;
    }

    private Map<String,String> fromByteToString(Map<byte[],byte[]> map) throws UnsupportedEncodingException {
        Map<String,String> resultMap = new HashMap<String, String>();
        Set<byte[]> keySet = map.keySet();
        for(byte[] key : keySet) {
            String value = new String(map.get(key),"UTF-8");
            String keyStr = new String(key,"UTF-8");
            resultMap.put(keyStr, value);
        }
        return resultMap;
    }

    private Map<String,Object> fromByteToStringAndObject(Map<byte[],byte[]> map) {
        Map<String,Object> resultMap = new HashMap<String, Object>();
        Set<byte[]> keySet = map.keySet();
        for(byte[] key : keySet) {
            Object value = new String(map.get(key));
            String keyStr = new String(key);
            resultMap.put(keyStr, value);
        }
        return resultMap;
    }

    /* (non-Javadoc)
     * @see tv.stardream.redis.IRedisOperateDAO#hkeyExist(java.lang.String)
     */
    @Override
    public boolean hkeyExist(final String key,final String hkey) {
        return (boolean)redisTemplate.execute(new RedisCallback<Object>() {
            @Override
            public Boolean doInRedis(RedisConnection connection)
                    throws DataAccessException {
                connection.select(dbIdx);
                boolean exists = connection.hExists(key.getBytes(),hkey.getBytes());
                return exists;
            }
        });
    }

    /* (non-Javadoc)
     * @see tv.stardream.redis.IRedisOperateDAO#hget(java.lang.String, java.lang.String)
     */
    @Override
    public String hget(final String key,final String hkey) {
        return (String)redisTemplate.execute(new RedisCallback<Object>() {
            @Override
            public String doInRedis(RedisConnection connection)
                    throws DataAccessException {
                connection.select(dbIdx);
                byte[] val = connection.hGet(key.getBytes(), hkey.getBytes());
                try {
                    return new String(val,"UTF-8");
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                }
                return null;
            }
        });
    }

    /* (non-Javadoc)
     * @see tv.stardream.redis.IRedisOperateDAO#hset(java.lang.String, java.lang.String, java.lang.String)
     */
    @Override
    public Boolean hset(final String key,final String hkey,final String hval) {
        return (Boolean)redisTemplate.execute(new RedisCallback<Object>() {
            @Override
            public Boolean doInRedis(RedisConnection connection)
                    throws DataAccessException {
                connection.select(dbIdx);
                Boolean rel = null;
                try {
                    rel = connection.hSet(key.getBytes(),hkey.getBytes(),hval.getBytes("UTF-8"));
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                }
                return rel;
            }
        });
    }

    /* (non-Javadoc)
     * @see tv.stardream.redis.IRedisOperateDAO#keys(java.lang.String)
     */
    @SuppressWarnings("unchecked")
    @Override
    public Set<String> keys(final String patten) {
        return (Set<String>)redisTemplate.execute(new RedisCallback<Object>() {
            @Override
            public Set<String> doInRedis(RedisConnection connection)
                    throws DataAccessException {
                connection.select(dbIdx);
                Set<byte[]> set = connection.keys(patten.getBytes());
                try {
                    return fromByteToString(set);
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                }
                return null;
            }
        });
    }

    /* (non-Javadoc)
     * @see tv.stardream.redis.IRedisOperateDAO#scard(java.lang.String)
     */
    @Override
    public long scard(final String key) {
        return (long)redisTemplate.execute(new RedisCallback<Object>() {
            @Override
            public Long doInRedis(RedisConnection connection)
                    throws DataAccessException {
                connection.select(dbIdx);
                long rel = connection.sCard(key.getBytes());
                return rel;
            }
        });
    }

    /* (non-Javadoc)
     * @see tv.stardream.redis.IRedisOperateDAO#sismember(java.lang.String, java.lang.String)
     */
    @Override
    public boolean sismember(final String key,final String mem) {
        return (Boolean)redisTemplate.execute(new RedisCallback<Object>() {
            @Override
            public Boolean doInRedis(RedisConnection connection)
                    throws DataAccessException {
                connection.select(dbIdx);
                Boolean rel = null;
                try {
                    rel = connection.sIsMember(key.getBytes(),mem.getBytes("UTF-8"));
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                }
                return rel;
            }
        });
    }

    /* (non-Javadoc)
     * @see tv.stardream.redis.IRedisOperateDAO#hkeys(java.lang.String)
     */
    @SuppressWarnings("unchecked")
    @Override
    public Set<String> hkeys(final String key) {
        return (Set<String>)redisTemplate.execute(new RedisCallback<Object>() {
            @Override
            public Set<String> doInRedis(RedisConnection connection)
                    throws DataAccessException {
                connection.select(dbIdx);
                Set<byte[]> set = connection.hKeys(key.getBytes());
                try {
                    return fromByteToString(set);
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                }
                return null;
            }
        });
    }

    @Override
    public List<String> hvals(final String key) {
        return redisTemplate.execute(new RedisCallback<List<String>>() {
            @Override
            public List<String> doInRedis(RedisConnection connection) throws DataAccessException {
                connection.select(dbIdx);
                List<byte[]> list = connection.hVals(key.getBytes());
                try {
                    return fromByteToString(list);
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                }
                return null;
            }
        });
    }

    /* (non-Javadoc)
     * @see tv.stardream.redis.IRedisOperateDAO#srem(java.lang.String, java.lang.String)
     */
    @Override
    public void srem(final String key,final String mem) {
        redisTemplate.execute(new RedisCallback<Object>() {
            @Override
            public Object doInRedis(RedisConnection connection)
                    throws DataAccessException {
                connection.select(dbIdx);
                try {
                    connection.sRem(key.getBytes(),mem.getBytes("UTF-8"));
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                }
                return null;
            }
        });
    }

    @Override
    public void expire(final String key, final long seconds) {
        redisTemplate.execute(new RedisCallback<Object>() {
            @Override
            public Object doInRedis(RedisConnection connection)
                    throws DataAccessException {
                connection.select(dbIdx);
                connection.expire(key.getBytes(), seconds);
                return null;
            }
        });
    }

    @Override
    public void lpush(final String key,final String val) {
        redisTemplate.execute(new RedisCallback<Object>() {
            @Override
            public Object doInRedis(RedisConnection connection) throws DataAccessException {
                connection.select(dbIdx);
                try {
                    connection.lPush(key.getBytes(),val.getBytes("UTF-8"));
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                }
                return null;
            }
        });
    }

    @Override
    public void rpush(final String key,final String val) {
        redisTemplate.execute(new RedisCallback<Object>() {
            @Override
            public Object doInRedis(RedisConnection connection) throws DataAccessException {
                connection.select(dbIdx);
                try {
                    connection.rPush(key.getBytes(), val.getBytes("UTF-8"));
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                }
                return null;
            }
        });
    }

    @Override
    public long llen(final String key) {
        return (long)redisTemplate.execute(new RedisCallback<Object>() {
            @Override
            public Long doInRedis(RedisConnection connection) throws DataAccessException {
                connection.select(dbIdx);

                long len = connection.lLen(key.getBytes());
                return len;
            }
        });
    }

    @Override
    public List<String> lrange(final String key,final Long start,final Long end) {
        return redisTemplate.execute(new RedisCallback<List<String>>() {
            @Override
            public List<String> doInRedis(RedisConnection connection) throws DataAccessException {
                connection.select(dbIdx);
                List<byte[]> list = connection.lRange(key.getBytes(),start,end);
                try {
                    return fromByteToString(list);
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                }
                return null;
            }
        });
    }

    @Override
    public void lset(final String key,final Long index,final String val) {
        redisTemplate.execute(new RedisCallback<Object>() {
            @Override
            public Object doInRedis(RedisConnection connection) throws DataAccessException {
                connection.select(dbIdx);
                try {
                    connection.lSet(key.getBytes(),index,val.getBytes("UTF-8"));
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                }
                return null;
            }
        });
    }

    @Override
    public String lindex(final String key,final Long index) {
        return  (String)redisTemplate.execute(new RedisCallback<Object>() {
            @Override
            public String doInRedis(RedisConnection connection) throws DataAccessException {
                connection.select(dbIdx);
                byte[] val = connection.lIndex(key.getBytes(),index);
                try {
                    return new String(val,"UTF-8");
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                }
                return null;
            }
        });
    }


    private List<String> fromByteToString(List<byte[]> set) throws UnsupportedEncodingException {
        List<String> resultList = new ArrayList<>();
        for(byte[] value : set) {
            resultList.add(new String(value,"UTF-8"));
        }
        return resultList;
    }

    @Override
    public boolean keyExists(final String key) {
        return (boolean)redisTemplate.execute(new RedisCallback<Object>() {
            @Override
            public Boolean doInRedis(RedisConnection connection)
                throws DataAccessException {
                connection.select(dbIdx);
                boolean exists = connection.exists(key.getBytes());
                return exists;
            }
        });
    }

    @Override
    public void sAdd(final String key,final String value) {
        redisTemplate.execute(new RedisCallback<Object>() {
            @Override
            public Object doInRedis(RedisConnection connection)
                throws DataAccessException {
                connection.select(dbIdx);
                connection.sAdd(key.getBytes(), value.getBytes());
                return null;
            }
        });
    }

    @Override
    @SuppressWarnings("unchecked")
    public Map<String,String> hGetAll(final String key) {
        return (Map<String,String>)redisTemplate.execute(new RedisCallback<Object>() {
            @Override
            public Map<String,String> doInRedis(RedisConnection connection)
                throws DataAccessException {
                connection.select(dbIdx);
                Map<String,String> map = null;
                try {
                    map = fromByteToString(connection.hGetAll(key.getBytes()));
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                }
                return map;
            }
        });
    }



}
